#!/bin/bash

set -euo pipefail

# 配置变量
CONTAINER_NAMES=("gougu" "gougubt")
IMAGE_NAMES=("mfkd1000/gougu" "mfkd1000/gougubt")
REGISTRY_URLS=("registry.cn-hangzhou.aliyuncs.com/mfkd1000/gougu" "registry.cn-hangzhou.aliyuncs.com/mfkd1000/gougubt")
LOG_FILE="/var/log/oa_upgrade_$(date +%Y%m%d_%H%M%S).log"
NETWORK_CHECK_HOST="registry-1.docker.io"

# 升级配置
UPGRADE_OPTION="1"  # 1=升级全部

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 重定向输出到日志文件
exec > >(tee -a "$LOG_FILE") 2>&1

# 错误处理函数
error_exit() {
    echo -e "${RED}错误: $1${NC}" >&2
    exit 1
}

# 信息输出函数
info() {
    echo -e "${BLUE}[INFO] $1${NC}"
}

success() {
    echo -e "${GREEN}[SUCCESS] $1${NC}"
}

warning() {
    echo -e "${YELLOW}[WARNING] $1${NC}"
}

# 手动录入用户名和密码
read_credentials() {
    echo "========================================"
    info "请输入 OA 系统升级凭据"
    echo "========================================"
    
    read -p "请输入用户名: " OA_USERNAME
    if [ -z "$OA_USERNAME" ]; then
        error_exit "用户名不能为空"
    fi
    
    read -s -p "请输入密码: " OA_PASSWORD
    echo
    if [ -z "$OA_PASSWORD" ]; then
        error_exit "密码不能为空"
    fi
    
    # 确认凭据
    echo
    info "您输入的用户名: $OA_USERNAME"
    read -p "确认凭据是否正确？(y/N): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        error_exit "用户取消操作"
    fi
    
    # 导出为全局变量
    export OA_USERNAME
    export OA_PASSWORD
}

# 网络连通性检查函数
check_network_connectivity() {
    local host=$1
    local timeout=60
    
    info "检查网络连通性: ping $host (超时: ${timeout}秒)..."
    
    # 使用ping命令检查网络连通性
    if timeout $timeout ping -c 2 -W 3 "$host" >/dev/null 2>&1; then
        success "网络连通性正常: 可以ping通 $host"
        return 0
    else
        warning "警告: 网络不通，无法ping通 $host"
        return 1
    fi
}

# 修改后的带超时的docker push函数
docker_push_with_network_check() {
    local image=$1
    local timeout=20  # 20秒超时

    # 检查网络连通性
    if check_network_connectivity "$NETWORK_CHECK_HOST"; then
        # 询问用户是否推送镜像到DockerHub，20秒超时自动跳过
        info "是否推送镜像 $image 到DockerHub？(y/N) - ${timeout}秒后自动跳过..."
        
        # 设置20秒超时读取用户输入
        if read -t $timeout -p "请选择 (y/N): " -n 1 -r; then
            echo  # 换行
            if [[ $REPLY =~ ^[Yy]$ ]]; then
                info "推送镜像 $image 到DockerHub..."
                if docker push "$image"; then
                    success "镜像 $image 推送成功"
                    return 0
                else
                    warning "推送镜像 $image 失败"
                    return 1
                fi
            else
                info "用户选择不推送镜像 $image 到DockerHub，继续执行..."
                return 0
            fi
        else
            # 超时情况
            echo  # 换行
            warning "${timeout}秒内未收到输入，自动跳过DockerHub推送，继续执行..."
            return 0
        fi
    else
        warning "网络不通，跳过推送镜像 $image 到DockerHub"
        return 0
    fi
}

# 版本获取函数
get_version() {
    local container=$1
    docker exec "$container" php -r "
        \$content = @file_get_contents('/www/wwwroot/office/public/index.php');
        if (\$content && preg_match('/define\(\'CMS_VERSION\',\s*\'([^\']+)\'\)/', \$content, \$matches)) {
            echo trim(\$matches[1]);
        } else {
            exit(1);
        }
    " 2>/dev/null || echo "unknown"
}

# 清理容器内垃圾函数
cleanup_container_garbage() {
    local container_name=$1
    
    info "开始清理容器 $container_name 内的垃圾文件..."
    
    # 检查容器状态
    if [ "$(docker inspect -f '{{.State.Running}}' "$container_name")" = "false" ]; then
        info "启动容器 $container_name 进行清理..."
        docker start "$container_name"
        sleep 3
    fi
    
    # 清理各种缓存和临时文件
    info "清理系统缓存和临时文件..."
    docker exec "$container_name" sh -c "
        # 清理包管理器缓存
        apt-get clean 2>/dev/null || true
        apt-get autoclean 2>/dev/null || true
        
        # 清理日志文件（保留最近7天的）
        find /var/log -name '*.log' -mtime +7 -delete 2>/dev/null || true
        find /var/log -name '*.gz' -delete 2>/dev/null || true
        find /var/log -name '*.old' -delete 2>/dev/null || true
        
        # 清理临时文件
        rm -rf /tmp/* /var/tmp/* 2>/dev/null || true
        
        # 清理浏览器缓存（如果有）
        find /home -name '.cache' -type d -exec rm -rf {} \; 2>/dev/null || true
        find /root -name '.cache' -type d -exec rm -rf {} \; 2>/dev/null || true
        
        # 清理PHP session和opcache
        rm -rf /var/lib/php/sessions/* 2>/dev/null || true
        rm -rf /var/lib/php/*/opcache/* 2>/dev/null || true
        
        # 清理nginx缓存
        rm -rf /var/cache/nginx/* 2>/dev/null || true
        
        # 清理备份文件和临时备份
        find /www -name '*.bak' -delete 2>/dev/null || true
        find /www -name '*.old' -delete 2>/dev/null || true
        find /www -name '*.tmp' -delete 2>/dev/null || true
        find /www -name '.DS_Store' -delete 2>/dev/null || true
        find /www -name 'Thumbs.db' -delete 2>/dev/null || true
        
        # 清理应用特定的临时文件
        rm -rf /www/wwwroot/office/runtime/* 2>/dev/null || true
        rm -rf /www/wwwroot/office/public/static/temp/* 2>/dev/null || true
        
        # 清理docker内部的无用文件
        rm -f /root/.bash_history 2>/dev/null || true
        rm -rf /etc/auth_system 2>/dev/null || true
        
        # 清理core dump文件
        find / -name 'core' -type f -delete 2>/dev/null || true
        find / -name '*.core' -type f -delete 2>/dev/null || true
        
        echo '容器垃圾清理完成'
    " || warning "容器 $container_name 垃圾清理过程中出现错误，继续执行"
    
    success "容器 $container_name 垃圾清理完成"
}

# 清理旧镜像函数
cleanup_old_images() {
    local container_name=$1
    local image_name=$2
    local registry_url=$3
    local old_version=$4
    
    info "开始为容器 $container_name 清理旧镜像..."
    
    # 清理主镜像标签
    docker rmi "$image_name" 2>/dev/null || info "镜像 $image_name 不存在或清理失败"
    docker rmi "$registry_url" 2>/dev/null || info "镜像 $registry_url 不存在或清理失败"
    
    # 清理旧版本标签（如果知道旧版本号）
    if [ "$old_version" != "unknown" ]; then
        docker rmi "${registry_url}:${old_version}" 2>/dev/null || info "镜像 ${registry_url}:${old_version} 不存在或清理失败"
    fi
    
    success "容器 $container_name 的旧镜像清理完成"
}

# 使用expect工具进行自动交互（如果可用）
auto_upgrade_with_expect() {
    local container_name=$1
    
    info "尝试使用expect进行自动交互升级..."
    
    # 检查是否安装了expect
    if command -v expect >/dev/null 2>&1; then
        local expect_script=$(mktemp)
        cat > "$expect_script" << EOF
#!/usr/bin/expect -f
set timeout 300

spawn docker exec -it $container_name ./gouguupdate.sh

expect "请输入用户名:"
send "$OA_USERNAME\r"

expect "请输入密码:"
send "$OA_PASSWORD\r"

expect "请选择升级选项："
send "$UPGRADE_OPTION\r"

expect eof
EOF
        
        chmod +x "$expect_script"
        if expect -f "$expect_script"; then
            rm -f "$expect_script"
            return 0
        else
            rm -f "$expect_script"
            return 1
        fi
    else
        warning "expect工具未安装，使用备用方法"
        return 1
    fi
}

# 备用方法：使用简单的输入重定向
auto_upgrade_simple() {
    local container_name=$1
    
    info "使用简单输入重定向进行升级..."
    
    # 创建输入文件
    local input_file=$(mktemp)
    {
        echo "$OA_USERNAME"    # 用户名
        echo "$OA_PASSWORD"    # 密码  
        echo "$UPGRADE_OPTION" # 升级选项
    } > "$input_file"
    
    # 使用输入重定向执行升级
    if docker exec -i "$container_name" ./gouguupdate.sh < "$input_file"; then
        rm -f "$input_file"
        return 0
    else
        rm -f "$input_file"
        return 1
    fi
}

# 自动交互升级函数
auto_upgrade_container() {
    local container_name=$1
    
    info "开始自动执行容器 $container_name 的升级..."
    
    # 首先尝试使用expect（如果可用）
    if auto_upgrade_with_expect "$container_name"; then
        success "使用expect自动升级完成"
        return 0
    fi
    
    # 如果expect不可用或失败，使用简单方法
    info "尝试使用简单输入重定向方法..."
    if auto_upgrade_simple "$container_name"; then
        success "使用简单输入重定向自动升级完成"
        return 0
    fi
    
    # 如果自动方法都失败，提示手动操作
    warning "自动升级方法失败，请手动执行以下命令完成升级:"
    echo "docker exec -it $container_name ./gouguupdate.sh"
    echo "然后依次输入:"
    echo "用户名: $OA_USERNAME"
    echo "密码: [已录入]" 
    echo "升级选项: $UPGRADE_OPTION"
    
    read -p "是否已手动完成升级？(按Y继续，其他键退出): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        error_exit "用户取消升级"
    fi
    
    return 0
}

# 升级单个容器函数
upgrade_container() {
    local container_name=$1
    local image_name=$2
    local registry_url=$3
    
    echo "========================================"
    info "开始处理容器: $container_name"
    echo "========================================"
    
    # 检查容器状态
    if ! docker inspect "$container_name" &>/dev/null; then
        warning "容器 $container_name 不存在，跳过处理"
        return 1
    fi

    if [ "$(docker inspect -f '{{.State.Running}}' "$container_name")" = "false" ]; then
        info "启动容器 $container_name..."
        docker start "$container_name"
        sleep 5
    fi

    # 获取当前版本
    info "获取容器 $container_name 的当前版本..."
    OLD_VERSION=$(get_version "$container_name")
    info "容器 $container_name 当前版本: $OLD_VERSION"

    # 清理容器内垃圾
    cleanup_container_garbage "$container_name"

    # 清理旧镜像
    cleanup_old_images "$container_name" "$image_name" "$registry_url" "$OLD_VERSION"

    # 执行自动升级
    auto_upgrade_container "$container_name"
    
    # 清理升级后的临时文件
    docker exec "$container_name" sh -c "rm -f /root/.bash_history && rm -rf /etc/auth_system" || warning "清理临时文件失败"

    # 再次清理升级后可能产生的垃圾
    info "升级后再次清理容器垃圾..."
    cleanup_container_garbage "$container_name"

    # 获取新版本
    info "获取容器 $container_name 的新版本..."
    NEW_VERSION=$(get_version "$container_name")
    info "容器 $container_name 新版本: $NEW_VERSION"

    if [ "$NEW_VERSION" = "unknown" ]; then
        error_exit "无法获取容器 $container_name 的新版本号"
    fi

    # 创建和推送新镜像
    info "为容器 $container_name 创建新镜像..."
    docker commit "$container_name" "$image_name" || error_exit "容器 $container_name 镜像创建失败"
    docker tag "$image_name" "$registry_url" || error_exit "容器 $container_name 镜像标签设置失败"
    docker tag "$image_name" "${registry_url}:${NEW_VERSION}" || error_exit "容器 $container_name 版本标签设置失败"

    info "推送容器 $container_name 的镜像到阿里云..."
    docker push "$registry_url" || warning "容器 $container_name 阿里云镜像推送失败，继续执行"
    docker push "${registry_url}:${NEW_VERSION}" || warning "容器 $container_name 阿里云版本镜像推送失败，继续执行"

    info "推送容器 $container_name 的镜像到DockerHub..."
    docker_push_with_network_check "$image_name"
    
    success "容器 $container_name 处理完成!"
    echo ""
}

# 显示容器选择菜单
show_container_menu() {
    echo "请选择要升级的容器 (可多选，用空格分隔):"
    for i in "${!CONTAINER_NAMES[@]}"; do
        echo "$((i+1)). ${CONTAINER_NAMES[$i]}"
    done
    echo "A. 所有容器"
    echo "Q. 退出"
}

# 获取用户选择的容器
get_selected_containers() {
    local selected_indices=()
    
    while true; do
        read -p "请输入选择 (例如: 1 2 或 A): " -r choice
        case $choice in
            [Aa])
                # 选择所有容器
                selected_indices=($(seq 0 $((${#CONTAINER_NAMES[@]}-1))))
                break
                ;;
            [Qq])
                echo "exit_selected"  # 特殊标记表示退出
                return 0
                ;;
            *)
                # 处理数字选择
                local valid=true
                selected_indices=()
                for num in $choice; do
                    if [[ $num =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le "${#CONTAINER_NAMES[@]}" ]; then
                        selected_indices+=($((num-1)))
                    else
                        echo -e "${RED}错误: 无效的选择 '$num'${NC}"
                        valid=false
                        break
                    fi
                done
                
                if [ "$valid" = true ] && [ ${#selected_indices[@]} -gt 0 ]; then
                    break
                else
                    echo -e "${YELLOW}请输入有效的选择${NC}"
                fi
                ;;
        esac
    done
    
    echo "${selected_indices[@]}"
}

# 主执行流程
echo "开始升级 OA 系统..."
echo "升级开始时间: $(date)"

# 手动录入用户名和密码
read_credentials

# 显示升级配置
info "升级配置:"
info "用户名: $OA_USERNAME"
info "升级选项: $UPGRADE_OPTION (升级全部)"

# 显示菜单并获取选择
show_container_menu
selection_result=$(get_selected_containers)

# 检查是否选择了退出
if [[ "$selection_result" == "exit_selected" ]]; then
    info "升级已取消"
    exit 0
fi

# 将选择结果转换为数组
selected_indices=($selection_result)

# 确认继续
info "您选择了以下容器:"
for index in "${selected_indices[@]}"; do
    echo "  - ${CONTAINER_NAMES[$index]}"
done

read -p "是否继续升级？(y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
    info "升级已取消"
    exit 0
fi

# 循环处理选择的容器
for index in "${selected_indices[@]}"; do
    upgrade_container "${CONTAINER_NAMES[$index]}" "${IMAGE_NAMES[$index]}" "${REGISTRY_URLS[$index]}"
done

# 清理悬空镜像（在所有容器处理完成后执行一次）
info "清理所有悬空镜像..."
docker image prune -f 2>/dev/null || true

success "所选容器升级完成!"
info "升级结束时间: $(date)"